package com.google.code.expr.lang;

import com.google.code.expr.runtime.EXPRNullPointerException;
import com.google.code.expr.runtime.EXPRRunTimeException;
import com.google.code.expr.runtime.EXPRRunTimeState;
import com.google.code.expr.runtime.EXPRValue;


public class EXPRExpression extends EXPROperand {

	private EXPROperand mOperand1;
	private EXPROperator mOperator;
	private EXPROperand mOperand2;
	
	public EXPRExpression() {
		super();
	}

	public void setOperand1(EXPROperand pOperand) {
		mOperand1 = pOperand;
	}
	
	public EXPROperand getOperand1() {
		return mOperand1;
	}

	
	public EXPROperator getOperator() {
		return mOperator;
	}

	public boolean hasOperator() {
		return mOperator!=null;
	}
	
	public void setOperatorAndOperand2(EXPROperator pOperator,EXPROperand pOperand2) {
		if (hasOperator()) {
			if (mOperator.hasHigherPrecedenceThan(pOperator)) {
				//the new operator has higher precedence
				//create an expression out of operand1 and the new
				//operator and operand, and assign it as operand1
				EXPRExpression newOperand1 = new EXPRExpression();
				newOperand1.setOperand1(mOperand1);
				newOperand1.setOperatorAndOperand2(mOperator, mOperand2);
				mOperand1 = newOperand1;
				mOperator = pOperator;
				mOperand2 = pOperand2;
			}
			else {
				//the new operator and operand is lower in precedence.  we need to
				//create an expression out of the existing operand2 and the new 
				//operator and operand, then assign it as operand2			
				EXPRExpression newOperand2 = new EXPRExpression();
				newOperand2.setOperand1(mOperand2);
				newOperand2.setOperatorAndOperand2(pOperator, pOperand2);
				mOperand2 = newOperand2;
			}
		}
		else {
			this.mOperator = pOperator;
			this.mOperand2 = pOperand2;
		}
	}

	public EXPROperand getOperand2() {
		return mOperand2;
	}


	public String toString() {
		StringBuffer buffer = new StringBuffer();
		buffer.append("(");
		buffer.append(mOperand1);
		if (hasOperator()) {
			buffer.append(" ");
			buffer.append(getOperator());
			buffer.append(" ");
			buffer.append(getOperand2());
		}
		buffer.append(")");
		return buffer.toString();
	}

	@Override
	public String resolveToString(EXPRRunTimeState pState) throws EXPRRunTimeException {
		EXPRValue resolved = this.resolveToValue(pState);
		return resolved!=null ? resolved.resolveAsString() : null;
	}
	
	public EXPRValue resolveToValue(EXPRRunTimeState pState) throws EXPRRunTimeException {
		if (!hasOperator()) {
			return mOperand1.resolveToValue(pState);
		}
		else {
			EXPRValue leftValue = mOperand1.resolveToValue(pState);
			EXPRValue rightValue = mOperand2.resolveToValue(pState);
			if (mOperator.equals(EXPROperator.AND)) {
				if (leftValue==null || rightValue==null) throw new EXPRNullPointerException(this);
				if (leftValue.isBoolean() && rightValue.isBoolean()) {
					if (leftValue.getBoolean().booleanValue() && rightValue.getBoolean().booleanValue()) {
						return new EXPRValue(Boolean.TRUE);
					}
				}
				return new EXPRValue(Boolean.FALSE);
			}
			else if (mOperator.equals(EXPROperator.OR)) {
				if (leftValue==null || rightValue==null) throw new EXPRNullPointerException(this);
				if (leftValue.isBoolean() && rightValue.isBoolean()) {
					if (leftValue.getBoolean().booleanValue() || rightValue.getBoolean().booleanValue()) {
						return new EXPRValue(Boolean.TRUE);
					}
				}
				return new EXPRValue(Boolean.FALSE);
			}
			else if (mOperator.equals(EXPROperator.XOR)) {
				if (leftValue==null || rightValue==null) throw new EXPRNullPointerException(this);
				if (leftValue.isBoolean() && rightValue.isBoolean()) {
					if (leftValue.getBoolean().booleanValue() ^ rightValue.getBoolean().booleanValue()) {
						return new EXPRValue(Boolean.TRUE);
					}
				}
				return new EXPRValue(Boolean.FALSE);
			}
			else if (mOperator.equals(EXPROperator.EQUALS) || mOperator.equals(EXPROperator.NOT_EQUALS)) {
				boolean result = false;
				if (leftValue==null && rightValue==null) {
					result=true;
				}
				else if (leftValue!=null && rightValue!=null) {
					result = leftValue.getValue().equals(rightValue.getValue());
				}
				if (mOperator.equals(EXPROperator.NOT_EQUALS)) result = !result;
				return new EXPRValue(result ? Boolean.TRUE : Boolean.FALSE);
			}
			else if (mOperator.equals(EXPROperator.LESS_THAN) || mOperator.equals(EXPROperator.LESS_EQUALS)
					 || mOperator.equals(EXPROperator.MORE_THAN) || mOperator.equals(EXPROperator.MORE_EQUALS)) {
				if (leftValue==null || rightValue==null) throw new EXPRNullPointerException(this);
				Number leftNumber = leftValue.resolveAsNumber();
				Number rightNumber = rightValue.resolveAsNumber();
				if (leftNumber!=null && rightNumber!=null) {
					if (mOperator.equals(EXPROperator.LESS_THAN)) {
						return new EXPRValue(leftNumber.doubleValue() < rightNumber.doubleValue());
					}
					else if (mOperator.equals(EXPROperator.LESS_EQUALS)) {
						return new EXPRValue(leftNumber.doubleValue() <= rightNumber.doubleValue());
					}
					else if (mOperator.equals(EXPROperator.MORE_THAN)) {
						return new EXPRValue(leftNumber.doubleValue() > rightNumber.doubleValue());
					}
					else if (mOperator.equals(EXPROperator.MORE_EQUALS)) {
						return new EXPRValue(leftNumber.doubleValue() >= rightNumber.doubleValue());
					}
				}
				return new EXPRValue(Boolean.FALSE);
			}
			else if (mOperator.equals(EXPROperator.PLUS) || mOperator.equals(EXPROperator.MINUS)
					 || mOperator.equals(EXPROperator.MULTIPLY) || mOperator.equals(EXPROperator.DIVIDE)) {
				if (leftValue==null || rightValue==null) throw new EXPRNullPointerException(this);
				Number leftNumber = leftValue.resolveAsNumber();
				Number rightNumber = rightValue.resolveAsNumber();
				if (leftNumber!=null && rightNumber!=null) {
					if (leftNumber instanceof Double || rightNumber instanceof Double) {
						if (mOperator.equals(EXPROperator.PLUS)) {
							return new EXPRValue(new Double(leftNumber.doubleValue() + rightNumber.doubleValue()));
						}
						else if (mOperator.equals(EXPROperator.MINUS)) {
							return new EXPRValue(new Double(leftNumber.doubleValue() - rightNumber.doubleValue()));
						}
						else if (mOperator.equals(EXPROperator.MULTIPLY)) {
							return new EXPRValue(new Double(leftNumber.doubleValue() * rightNumber.doubleValue()));
						}
						else if (mOperator.equals(EXPROperator.DIVIDE)) {
							return new EXPRValue(new Double(leftNumber.doubleValue() / rightNumber.doubleValue()));
						}
					}
					else {
						if (mOperator.equals(EXPROperator.PLUS)) {
							return new EXPRValue(new Long(leftNumber.longValue() + rightNumber.longValue()));
						}
						else if (mOperator.equals(EXPROperator.MINUS)) {
							return new EXPRValue(new Long(leftNumber.longValue() - rightNumber.longValue()));
						}
						else if (mOperator.equals(EXPROperator.MULTIPLY)) {
							return new EXPRValue(new Long(leftNumber.longValue() * rightNumber.longValue()));
						}
						else if (mOperator.equals(EXPROperator.DIVIDE)) {
							return new EXPRValue(new Long(leftNumber.longValue() / rightNumber.longValue()));
						}
					}
				}
				else if (leftNumber!=null) {
					return new EXPRValue(leftNumber);
				}
				else if (rightNumber!=null) {
					return new EXPRValue(rightNumber);
				}
				else {
					return new EXPRValue(new Long(0L));
				}
			}
			return null;
		}
	}
}
